{{>licenseInfo}}
package {{package}}

import java.text.SimpleDateFormat

{{#imports}}import {{import}}
{{/imports}}
import {{invokerPackage}}.{ApiInvoker, ApiException}

import collection.mutable
import com.sun.jersey.multipart.FormDataMultiPart
import com.sun.jersey.multipart.file.FileDataBodyPart
import com.wordnik.swagger.client._
import com.wordnik.swagger.client.ClientResponseReaders.Json4sFormatsReader._
import com.wordnik.swagger.client.RequestWriters.Json4sFormatsWriter._
import javax.ws.rs.core.Response.Status.Family

import java.net.URI
import java.io.File
import java.util.Date
import java.util.TimeZone
import javax.ws.rs.core.{MediaType, Response}

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent._
import scala.concurrent.duration._
import scala.collection.mutable.HashMap
import scala.util.{Failure, Success, Try}

import org.json4s._

{{#operations}}
class {{classname}}(
  val defBasePath: String = "{{{basePath}}}",
  defApiInvoker: ApiInvoker = ApiInvoker
) {
  private lazy val dateTimeFormatter = {
    val formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
    formatter.setTimeZone(TimeZone.getTimeZone("UTC"))
    formatter
  }
  private val dateFormatter = {
    val formatter = new SimpleDateFormat("yyyy-MM-dd")
    formatter.setTimeZone(TimeZone.getTimeZone("UTC"))
    formatter
  }
  implicit val formats = new org.json4s.DefaultFormats {
    override def dateFormatter = dateTimeFormatter
  }
  implicit val stringReader: ClientResponseReader[String] = ClientResponseReaders.StringReader
  implicit val unitReader: ClientResponseReader[Unit] = ClientResponseReaders.UnitReader
  implicit val jvalueReader: ClientResponseReader[JValue] = ClientResponseReaders.JValueReader
  implicit val jsonReader: ClientResponseReader[Nothing] = JsonFormatsReader
  implicit val stringWriter: RequestWriter[String] = RequestWriters.StringWriter
  implicit val jsonWriter: RequestWriter[Nothing] = JsonFormatsWriter

  var basePath: String = defBasePath
  var apiInvoker: ApiInvoker = defApiInvoker

  def addHeader(key: String, value: String): mutable.HashMap[String, String] = {
    apiInvoker.defaultHeaders += key -> value
  }

  val config: SwaggerConfig = SwaggerConfig.forUrl(new URI(defBasePath))
  val client = new RestClient(config)
  val helper = new {{classname}}AsyncHelper(client, config)

  {{#operation}}
  /**
   * {{summary}}
   * {{notes}}
   *
{{#allParams}}   * @param {{paramName}} {{description}} {{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}
{{/allParams}}   * @return {{{returnType}}}{{^returnType}}void{{/returnType}}
   */
  def {{operationId}}({{#allParams}}{{paramName}}: {{#required}}{{dataType}}{{/required}}{{^required}}Option[{{dataType}}] = None{{/required}}{{^-last}}, {{/-last}}{{/allParams}}){{#returnType}}: Option[{{.}}]{{/returnType}} = {
    val await = Try(Await.result({{operationId}}Async({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}), Duration.Inf))
    await match {
      case Success(i) => Some(await.get)
      case Failure(t) => None
    }
  }

  /**
   * {{summary}} asynchronously
   * {{notes}}
   *
{{#allParams}}   * @param {{paramName}} {{description}} {{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}
{{/allParams}}   * @return Future({{{returnType}}}{{^returnType}}void{{/returnType}})
   */
  def {{operationId}}Async({{#allParams}}{{paramName}}: {{#required}}{{dataType}}{{/required}}{{^required}}Option[{{dataType}}] = None{{/required}}{{^-last}}, {{/-last}}{{/allParams}}){{#returnType}}: Future[{{.}}]{{/returnType}} = {
      helper.{{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}})
  }

  {{/operation}}
}

class {{classname}}AsyncHelper(client: TransportClient, config: SwaggerConfig) extends ApiClient(client, config) {

{{#operation}}
  def {{operationId}}({{#allParams}}{{^required}}{{paramName}}: Option[{{dataType}}] = None{{^-last}},{{/-last}}
    {{/required}}{{#required}}{{paramName}}: {{dataType}}{{^-last}},
    {{/-last}}{{/required}}{{/allParams}})(implicit reader: ClientResponseReader[{{returnType}}{{^returnType}}Unit{{/returnType}}]{{#bodyParams}}, writer: RequestWriter[{{^required}}Option[{{dataType}}]{{/required}}{{#required}}{{dataType}}{{/required}}]{{/bodyParams}}){{#returnType}}: Future[{{.}}]{{/returnType}}{{^returnType}}: Future[Unit]{{/returnType}} = {
    // create path and map variables
    val path = (addFmt("{{path}}"){{#pathParams}}
      replaceAll("\\{" + "{{baseName}}" + "\\}", {{paramName}}.toString){{/pathParams}})

    // query params
    val queryParams = new mutable.HashMap[String, String]
    val headerParams = new mutable.HashMap[String, String]

    {{#allParams}}
      {{#required}}
        {{^isPrimitiveType}}
    if ({{paramName}} == null) throw new Exception("Missing required parameter '{{paramName}}' when calling {{classname}}->{{operationId}}")
        {{/isPrimitiveType}}
        {{#isString}}
    if ({{paramName}} == null) throw new Exception("Missing required parameter '{{paramName}}' when calling {{classname}}->{{operationId}}")

        {{/isString}}
      {{/required}}
    {{/allParams}}
    {{#queryParams}}
      {{^required}}
    {{paramName}} match {
      case Some(param) => queryParams += "{{baseName}}" -> param.toString
      case _ => queryParams
    }
      {{/required}}
      {{#required}}
    queryParams += "{{baseName}}" -> {{paramName}}.toString
      {{/required}}
    {{/queryParams}}
    {{#headerParams}}
      {{^required}}
    {{paramName}} match {
      case Some(param) => headerParams += "{{baseName}}" -> param.toString
      case _ => headerParams
    }
      {{/required}}
      {{#required}}
    headerParams += "{{baseName}}" -> {{paramName}}.toString
      {{/required}}
    {{/headerParams}}

    val resFuture = client.submit("{{httpMethod}}", path, queryParams.toMap, headerParams.toMap, {{#bodyParam}}writer.write({{paramName}}){{/bodyParam}}{{^bodyParam}}"{{emptyBodyParam}}"{{/bodyParam}})
    resFuture flatMap { resp =>
      val status = Response.Status.fromStatusCode(resp.statusCode)
      status.getFamily match {
        case Family.SUCCESSFUL | Family.REDIRECTION | Family.INFORMATIONAL => process(reader.read(resp))
        case _ => throw new ApiException(resp.statusCode, resp.statusText)
      }
    }
  }

{{/operation}}

}
{{/operations}}
